package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.*;
import cn.mw.cmdb.enums.MonitorModeType;
import cn.mw.cmdb.param.SpecificationSearchParam;
import cn.mw.cmdb.param.TemplateExcelImportParam;
import cn.mw.cmdb.param.VendorSearchParam;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.util.ExcelUtils;
import cn.mw.components.mongodb.entity.SearchParam;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.google.common.base.Strings;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.mw.cmdb.util.ValConvertUtil.strValueConvert;

@Slf4j
public class TemplateExcelImportListener extends AnalysisEventListener<TemplateExcelImportParam> {

    private final HttpServletResponse response;

    private CmdbServiceManage cmdbServiceManage;

    /**
     * 文件名称
     */
    private String fileName;

    /**
     * 操作人员名称
     */
    private String operatorName;

    /**
     * 每隔100条存储数据库，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 解析成功的数据
     */
    List<TemplateExcelImportParam> list = new ArrayList<>();

    /**
     * 解析失败的数据
     */
    List<TemplateExcelImportParam> failList;

    public TemplateExcelImportListener(HttpServletResponse response, String fileName, String operatorName, CmdbServiceManage cmdbServiceManage,List<TemplateExcelImportParam> failList) {
        this.response = response;
        this.fileName = fileName;
        this.operatorName = operatorName;
        this.cmdbServiceManage = cmdbServiceManage;
        this.failList = failList;
    }

    /**
     * When analysis one row trigger invoke function.
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @SneakyThrows
    @Override
    public void invoke(TemplateExcelImportParam data, AnalysisContext context) {
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
        }
    }

    /**
     * if have something to do after all analysis
     *
     * @param context
     */
    @SneakyThrows
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        if (list.size() > 0) {
            saveData();
            log.info("所有数据解析完成！");
        }
        if (failList.size() > 0) {
            excelFailData(failList);
            log.info("解析失败的所数据导出完成");
        }
    }

    /**
     * 导出错误数据至excel文件里
     *
     * @param failList 错误数据
     */
    private void excelFailData(List<TemplateExcelImportParam> failList) {
        List<String> includeColumnFiledNames = new ArrayList<>();
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getBrand));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getSpecification));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getMonitorModeName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getSystemObjid));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getTemplateName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getModelGroupName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getModelName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getDescription));
        includeColumnFiledNames.add(CMDBTool.getFieldName(TemplateExcelImportParam::getErrorMsg));
        ExcelWriter excelWriter = null;
        String failFileName = fileName.indexOf(".") != -1 ? fileName.substring(0, fileName.indexOf(".")) : fileName;
        failFileName = "error_" + failFileName;
        try {
            excelWriter = ExcelUtils.getExcelWriter(failFileName, response, TemplateExcelImportParam.class);
            WriteSheet sheet = EasyExcel.writerSheet(0, "sheet" + 0)
                    .includeColumnFiledNames(includeColumnFiledNames)
                    .registerWriteHandler(ExcelUtils.writeTemplateStyle())
                    .registerWriteHandler(new ExcelUtils.ExcelColumWithStyle())
                    .build();
            excelWriter.write(failList, sheet);
            log.info("导出成功");
        } catch (IOException e) {
            log.error("导出失败{}", e);
        } finally {
            if (null != excelWriter) {
                excelWriter.finish();
            }
        }
    }

    /**
     * 保存数据
     */
    private void saveData() throws Exception {
        List<ModelTemplateInfo> templateInfos = new ArrayList<>();
        //**** 1:获取所有校验数据 ****


        ModelTemplateServiceImpl tempService = (ModelTemplateServiceImpl) cmdbServiceManage.getModelService(ModelTemplateServiceImpl.ID);
        List<ModelTemplateInfo> allTempList = tempService.getAllList();
        //模板特征信息校验Map
        Map<String, Long> descTempCheck = allTempList.stream().filter(s -> !Strings.isNullOrEmpty(s.getDescription())).collect(Collectors.groupingBy(s -> s.getTemplateName() + s.getDescription() + s.getSpecificationId(), Collectors.counting()));
        //模板OID信息校验Map
        Map<String, Long> oidTempCheck = allTempList.stream().filter(s -> !Strings.isNullOrEmpty(s.getSystemObjid())).collect(Collectors.groupingBy(s -> s.getTemplateName() + s.getSystemObjid() + s.getSpecificationId(), Collectors.counting()));

        //获取所有模型信息
        CustomModelServiceImpl modelService = (CustomModelServiceImpl) cmdbServiceManage.getModelService(CustomModelServiceImpl.ID);
        List<ModelInfo> modelInfos = modelService.findAllList();
        Map<String, String> modelMap = modelInfos.stream().filter(s -> !Strings.isNullOrEmpty(s.getModelName())).collect(Collectors.toMap(s -> s.getModelName(), s -> s.getId(), (
                value1, value2) -> {
            return value2;
        }));
        //获取所有分组信息
        ModelGroupServiceImpl groupService = (ModelGroupServiceImpl) cmdbServiceManage.getModelService(ModelGroupServiceImpl.ID);
        List<ModelGroupInfo> groupInfos = groupService.doSelectList(new SearchParam());
        Map<String, String> groupMap = groupInfos.stream().filter(s -> !Strings.isNullOrEmpty(s.getName())).collect(Collectors.toMap(s -> s.getName(), s -> s.getId(), (
                value1, value2) -> {
            return value2;
        }));
        //获取所有厂商信息
        ModelVendorServiceImpl vendorService = (ModelVendorServiceImpl) cmdbServiceManage.getModelService(ModelVendorServiceImpl.ID);
        List<ModelVendorInfo> vendorInfoList = vendorService.doSelectList(new VendorSearchParam());
        Map<String, String> vendorMap = vendorInfoList.stream().filter(s -> !Strings.isNullOrEmpty(s.getVendorCN())).collect(Collectors.toMap(s -> s.getVendorCN().toLowerCase(), s -> s.getId(), (
                value1, value2) -> {
            return value2;
        }));
        //获取所有规格信号信息
        ModelSpecificationServiceImpl specService = (ModelSpecificationServiceImpl) cmdbServiceManage.getModelService(ModelSpecificationServiceImpl.ID);
        List<ModelSpecificationInfo> specInfoList = specService.doSelectList(new SpecificationSearchParam());
        Map<String, String> specMap = specInfoList.stream().filter(s -> !Strings.isNullOrEmpty(s.getSpecification())).collect(Collectors.toMap(s -> s.getSpecification().toLowerCase(), s -> s.getId(), (
                value1, value2) -> {
            return value2;
        }));
        List<ModelTemplateInfo> tempList = new ArrayList<>();
        //**** 2:数据校验 ****
        log.info("{}条数据，开始添加模板数据！", list.size());
        if (list.size() > 0) {
            for (TemplateExcelImportParam importParam : list) {
                ModelTemplateInfo modelTempInfo = new ModelTemplateInfo();

                modelTempInfo = JSONObject.parseObject(JSONObject.toJSONString(importParam), ModelTemplateInfo.class);

                StringBuffer sbf = new StringBuffer();
                if (groupMap != null && groupMap.containsKey(importParam.getModelGroupName())) {
                    modelTempInfo.setModelGroupId(groupMap.get(importParam.getModelGroupName()));
                } else {
                    sbf.append("资产类型不存在");
                    sbf.append(";");
                }
                if (modelMap != null && modelMap.containsKey(importParam.getModelName())) {
                    modelTempInfo.setModelId(modelMap.get(importParam.getModelName()));
                } else {
                    sbf.append("子类型不存在");
                    sbf.append(";");
                }
                if (vendorMap != null && vendorMap.containsKey(strValueConvert(importParam.getBrand()).toLowerCase())) {
                    modelTempInfo.setBrandId(vendorMap.get(strValueConvert(importParam.getBrand()).toLowerCase()));
                } else {
                    sbf.append("厂商名称不存在");
                    sbf.append(";");
                }
                if (specMap != null && specMap.containsKey(strValueConvert(importParam.getSpecification()).toLowerCase())) {
                    modelTempInfo.setSpecificationId(specMap.get(strValueConvert(importParam.getSpecification()).toLowerCase()));
                } else {
                    sbf.append("规格型号不存在");
                    sbf.append(";");
                }
                if (MonitorModeType.getMonitorModel(importParam.getMonitorModeName()) != null) {
                    modelTempInfo.setMonitorMode(MonitorModeType.getMonitorModel(importParam.getMonitorModeName()));
                } else {
                    sbf.append("监控方式不存在");
                    sbf.append(";");
                }
                modelTempInfo.setStatus(true);
                String tempName = importParam.getTemplateName();
                String specificationId = modelTempInfo.getSpecificationId();
                String description = importParam.getDescription();
                String systemObjid = importParam.getSystemObjid();
                if (descTempCheck != null && descTempCheck.containsKey(tempName + description + specificationId)) {
                    sbf.append("模板特征信息重复");
                    sbf.append(";");
                }
                if (oidTempCheck != null && oidTempCheck.containsKey(tempName + systemObjid + specificationId)) {
                    sbf.append("模板oid信息重复");
                    sbf.append(";");
                }
                if (!Strings.isNullOrEmpty(sbf.toString())) {
                    importParam.setErrorMsg(sbf.toString());
                    failList.add(importParam);
                    continue;
                }
                tempList.add(modelTempInfo);
            }
        }
        //**** 3:导入数据 ****
        if (CollectionUtils.isNotEmpty(tempList)) {
            tempService.doBatchAdd(tempList);
        }
        log.info("{}条数据，导入失败数据！", failList.size());
        log.info("{}条数据，添加模板数据结束！", tempList.size());
    }

}
